#include "server.h"


int epollfd;

// 文件描述符，设置非阻塞模式
int setnonblocking( int fd )
{
    int old_option = fcntl( fd, F_GETFL );
    int new_option = old_option | O_NONBLOCK;
    fcntl( fd, F_SETFL, new_option );
    return old_option;
}

// 将 fd 添加到 epoll,是否设置 et 模式
void addfd( int epollfd, int fd, bool enable_et )
{
    static int count = 0;
    // 构造 epoll 事件
    struct epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN;
    if( enable_et )
    {
        event.events |= EPOLLET;
    }
    //注册 epoll 事件
    epoll_ctl( epollfd, EPOLL_CTL_ADD, fd, &event );
    setnonblocking( fd );
    printf("# addfd %d ok! \n",count++);
}

// 将 fd 从 epoll 内核表删除
void delfd(int epollfd, int fd)
{
    int ret = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
        if(ret == -1) {
            perror("epoll_ctl del cfd error");
            exit(1);
    }
}

/*
    接受客户端消息
*/
void ReceiveMsg(thFunctionArg* arg)
{
    // 2.1 读取客户端消息，直到取完缓冲区中的消息
    int ret = recv( arg->sockfd, &(arg->msg), sizeof(msgdata), 0 );
//    printMsg(&arg->msg);

    if( ret < 0 )
    {
        // would block: 可能堵塞
        if( ( errno == EAGAIN ) || ( errno == EWOULDBLOCK ) )
        {
            // 等下次触发再读
            //printf( "# error: read later\n" );
            printf("\n");
        }
        // 其他错误
        else close( arg->sockfd );
    }
    // 客户端断开连接
    else if( ret == 0 )
    {
        printf("# %s underline!\n",searchOnlineListAcc(arg->sockfd));
        // 从epoll内核表中删除
        delfd(arg->epollfd,arg->sockfd);
        delOnlineList(arg->sockfd);
        close( arg->sockfd );
    }
    // 读到数据，添加事件
    else threadpool_add(thp,ClientMsgProcess,arg);
}

/*
    处理事件：
        1. 边沿触发：只在接受到数据时触发，不管接受缓冲区是否有数据剩余
        2. 当事件触发的时候，把缓冲区数据全部读出来即可
*/
void et_mode( struct epoll_event* events, int number, int epollfd, int listenfd )
{
    int i;

    // 遍历就绪事件
    for ( i = 0; i < number; i++ )
    {
        int sockfd = events[i].data.fd;

        thFunctionArg* arg = NULL;
        arg = (thFunctionArg*)malloc(sizeof(thFunctionArg));

        if(arg == NULL)
        {
            perr_exit("malloc");
        }
        arg->sockfd = sockfd;
        arg->epollfd = epollfd;

        // 新客户端接入
        if(sockfd == listenfd)
        {
            // 添加任务
            threadpool_add(thp,acceptProcess,arg);
        }
        // 客户端消息
        else if ( events[i].events & EPOLLIN )
        {
            ReceiveMsg(arg);
        }
    }
}


/*
    运行服务器
*/
int RunServer()
{
    printf("# Server Runing...\n");

    //初始化
    initOnlineList();
    // 初始化缓冲区
    memset(&chatRecordList,NULL,CHAT_RECORDS);

    int ret = 0;
    struct sockaddr_in address;
    bzero( &address, sizeof( address ) );
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = htonl(INADDR_ANY);
    address.sin_port = htons( PORT );

    // 1.
    int listenfd = socket( PF_INET, SOCK_STREAM, 0 );
    assert( listenfd >= 0 );
    // 2.
    ret = bind( listenfd, ( struct sockaddr* )&address, sizeof( address ) );
    assert( ret != -1 );
    // 3.
    ret = listen( listenfd, MAX_EVENT_NUMBER );
    assert( ret != -1 );

    // 4.
    // 4.1 有事件发生的 `事件/fd` 集合：由内核设置
    struct epoll_event events[ MAX_EVENT_NUMBER ];

    // 4.2 epoll 注册事件表(在内核中)：由用户设置
    epollfd = epoll_create( MAX_EVENT_NUMBER );
    assert( epollfd != -1 );

    // 4.3 将 listenfd 注册到 epoll 事件表
    addfd( epollfd, listenfd, true );

    // 5. 创建线程池，池里最小3个线程，最大20，队列最大100
    thp = threadpool_create(3,20,100);

    while( 1 )
    {
        /*                
            最后一个参数(timeout)：-1 没有事件永远阻塞
                                    0 立即返回
        */
        int ret = epoll_wait( epollfd, events, MAX_EVENT_NUMBER, -1 );
        if ( ret < 0 )
        {
            printf( "# epoll failure\n" );
            break;
        }
        et_mode( events, ret, epollfd, listenfd );
    }

    close( listenfd );
    threadpool_destroy(thp);
    return 0;
}
